/*
 ------------------------------------------------------------------------------
 Copyright (C) 2009-2011 Eternal Games.

 This file is part of the Eternal Tech source code.

 The Eternal Tech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Eternal Tech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Eternal Tech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// cl_load.cpp - Loads all the game media assets
//


#include "cl_local.h"
#include "client.h"


/*
 ==============================================================================

    REGISTRATION

 ==============================================================================
*/


/*
 ==================
 CL_UpdateLoading
 ==================
*/
static void CL_UpdateLoading (const char *string)
{
	Str_Copy(cls.loadingInfo.string, string, sizeof(cls.loadingInfo.string));
	cls.loadingInfo.percent += 10;

	Win_GetEvents();
	CL_UpdateScreen();
}

/*
 ==================
 CL_RegisterCollisionMap
 ==================
*/
static void CL_RegisterCollisionMap ()
{
	uint	checksum;

	CL_UpdateLoading("COLLISION MAP");

	CM_LoadMap(cl.configStrings[CS_MODELS+1], true, &checksum);

	if (checksum != Str_ToInteger(cl.configStrings[CS_MAPCHECKSUM]))
		Com_Error(ERR_DROP, "Local map version differs from server: %i != %s", checksum, cl.configStrings[CS_MAPCHECKSUM]);
}

/*
 ==================
 CL_RegisterSounds
 ==================
*/
static void CL_RegisterSounds ()
{
	int		i;

	// Register sounds
	CL_UpdateLoading("SOUNDS");

	cl.media.sfxRichotecs[0] = S_RegisterSound("world/ric1.wav");
	cl.media.sfxRichotecs[1] = S_RegisterSound("world/ric2.wav");
	cl.media.sfxRichotecs[2] = S_RegisterSound("world/ric3.wav");
	cl.media.sfxSparks[0] = S_RegisterSound("world/spark5.wav");
	cl.media.sfxSparks[1] = S_RegisterSound("world/spark6.wav");
	cl.media.sfxSparks[2] = S_RegisterSound("world/spark7.wav");
	cl.media.sfxFootSteps[0] = S_RegisterSound("player/step1.wav");
	cl.media.sfxFootSteps[1] = S_RegisterSound("player/step2.wav");
	cl.media.sfxFootSteps[2] = S_RegisterSound("player/step3.wav");
	cl.media.sfxFootSteps[3] = S_RegisterSound("player/step4.wav");
	cl.media.sfxLaserHit = S_RegisterSound("weapons/lashit.wav");
	cl.media.sfxRailgun = S_RegisterSound("weapons/railgf1a.wav");
	cl.media.sfxRocketExplosion = S_RegisterSound("weapons/rocklx1a.wav");
	cl.media.sfxGrenadeExplosion = S_RegisterSound("weapons/grenlx1a.wav");
	cl.media.sfxWaterExplosion = S_RegisterSound("weapons/xpld_wat.wav");
#if 0 // FIXME: Not needed?
	if (!Str_ICompare(cl.gameDir, "rogue"))
	{
		cl.media.sfxLightning = S_RegisterSound("weapons/tesla.wav");
		cl.media.sfxDisruptorExplosion = S_RegisterSound("weapons/disrupthit.wav");
	}
#endif
	S_RegisterSound("player/land1.wav");
	S_RegisterSound("player/fall2.wav");
	S_RegisterSound("player/fall1.wav");

	// Register the sounds that the server references
	CL_UpdateLoading("GAME SOUNDS");

	for (i = 1; i < MAX_SOUNDS; i++)
	{
		if (!cl.configStrings[CS_SOUNDS+i][0])
			break;
	
		cl.media.gameSounds[i] = S_RegisterSound(cl.configStrings[CS_SOUNDS+i]);
	}
}

/*
 ==================
 CL_RegisterGraphics
 ==================
*/
static void CL_RegisterGraphics ()
{
	char	skyName[MAX_PATH_LENGTH];
	float   skyRotate;
	vec3_t	skyAxis;
	char	name[MAX_PATH_LENGTH];
	int		i;

	// Load the map
	CL_UpdateLoading("MAP");

	Str_SPrintf(skyName, sizeof(skyName), "env/%s", cl.configStrings[CS_SKY]);
	skyRotate = Str_ToFloat(cl.configStrings[CS_SKYROTATE]);

	sscanf(cl.configStrings[CS_SKYAXIS], "%f %f %f", &skyAxis[0], &skyAxis[1], &skyAxis[2]);

	R_LoadWorldMap(cl.configStrings[CS_MODELS+1], skyName, skyRotate, skyAxis);

	// Register models
	CL_UpdateLoading("MODELS");

	cl.media.modParasiteBeam = R_RegisterModel("models/monsters/parasite/segment/tris.md2");
	cl.media.modPowerScreenShell = R_RegisterModel("models/items/armor/effect/tris.md2");

	R_RegisterModel("models/objects/laser/tris.md2");
	R_RegisterModel("models/objects/grenade2/tris.md2");
	R_RegisterModel("models/weapons/v_machn/tris.md2");
	R_RegisterModel("models/weapons/v_handgr/tris.md2");
	R_RegisterModel("models/weapons/v_shotg2/tris.md2");
	R_RegisterModel("models/objects/gibs/bone/tris.md2");
	R_RegisterModel("models/objects/gibs/sm_meat/tris.md2");
	R_RegisterModel("models/objects/gibs/bone2/tris.md2");

	// Register the models that the server references
	CL_UpdateLoading("GAME MODELS");

	Str_Copy(cl.weaponModels[0], "weapon", sizeof(cl.weaponModels[0]));
	cl.numWeaponModels = 1;

	for (i = 1; i < MAX_MODELS2; i++)
	{
		if (!cl.configStrings[CS_MODELS+i][0])
			break;

		if (cl.configStrings[CS_MODELS+i][0] == '#')
		{
			// Special player weapon model
			if (cl.numWeaponModels < MAX_CLIENTWEAPONMODELS)
			{
				Com_StripExtension(cl.configStrings[CS_MODELS+i]+1, cl.weaponModels[cl.numWeaponModels], sizeof(cl.weaponModels[cl.numWeaponModels]));
				cl.numWeaponModels++;
			}
		} 
		else
		{
			Com_FileExtension(cl.configStrings[CS_MODELS+i], name, sizeof(name));
			if (!Str_ICompare(name, ".sp2"))
			{
				cl.media.gameModels[i] = NULL;
				continue;
			}

			cl.media.gameModels[i] = R_RegisterModel(cl.configStrings[CS_MODELS+i]);

			if (cl.configStrings[CS_MODELS+i][0] == '*')
				cl.media.gameCModels[i] = CM_InlineModel(cl.configStrings[CS_MODELS+i]);
			else
				cl.media.gameCModels[i] = NULL;
		}
	}

	// Register materials
	CL_UpdateLoading("MATERIALS");

	for (i = 0; i < NUM_CROSSHAIRS; i++)
		cl.media.crosshairMaterials[i] = R_RegisterMaterialNoMip(Str_VarArgs("pics/crosshair%i", i+1));

	cl.media.invulnerabilityShellMaterial = R_RegisterMaterial("shells/invulnerability", false);
	cl.media.quadDamageShellMaterial = R_RegisterMaterial("shells/quadDamage", false);
	cl.media.doubleDamageShellMaterial = R_RegisterMaterial("shells/doubleDamage", false);
	cl.media.halfDamageShellMaterial = R_RegisterMaterial("shells/halfDamage", false);
	cl.media.genericShellMaterial = R_RegisterMaterial("shells/generic", false);
	cl.media.powerScreenShellMaterial = R_RegisterMaterial("shells/powerScreen", false);

	// Register the materials that the server references
	CL_UpdateLoading("GAME MATERIALS");

	for (i = 1; i < MAX_IMAGES; i++)
	{
		if (!cl.configStrings[CS_IMAGES+i][0])
			break;

		if (!strchr(cl.configStrings[CS_IMAGES+i], '/'))
			Str_SPrintf(name, sizeof(name), "pics/%s", cl.configStrings[CS_IMAGES+i]);
		else
			Com_StripExtension(cl.configStrings[CS_IMAGES+i], name, sizeof(name));

		cl.media.gameMaterials[i] = R_RegisterMaterialNoMip(name);
	}

	// Register particles
	cl.media.blasterTrailParticle = CL_RegisterParticle("blasterTrail");
	cl.media.grenadeTrailParticle = CL_RegisterParticle("grenadeTrail");
	cl.media.rocketTrailParticle = CL_RegisterParticle("rocketTrail");
	cl.media.rocketTrailParticle2 = CL_RegisterParticle("rocketTrail2");
	cl.media.railTrailParticle = CL_RegisterParticle("railTrail");
	cl.media.railTrailParticle2 = CL_RegisterParticle("railTrail2");
	cl.media.bubbleTrailParticle = CL_RegisterParticle("bubbleTrail");
	cl.media.blasterParticle = CL_RegisterParticle("blasterParticle");
	cl.media.blasterParticle2 = CL_RegisterParticle("blasterParticle2");
	cl.media.bulletParticle = CL_RegisterParticle("bulletParticle");
	cl.media.bulletParticle2 = CL_RegisterParticle("bulletParticle2");
	cl.media.tracerParticle = CL_RegisterParticle("tracerParticle");
	cl.media.explotionParticle = CL_RegisterParticle("explotionParticle");
	cl.media.explotionParticle2 = CL_RegisterParticle("explotionParticle2");
	cl.media.smokePuffParticle = CL_RegisterParticle("smokePuffParticle");
	cl.media.bubbleParticle = CL_RegisterParticle("bubbleParticle");
	cl.media.sparkParticle = CL_RegisterParticle("sparkParticle");
	cl.media.sparkParticle2 = CL_RegisterParticle("sparkParticle2");
	cl.media.damageSparkParticle = CL_RegisterParticle("damageSparkParticle");
}

/*
 ==================
 CL_RegisterClients
 ==================
*/
static void CL_RegisterClients ()
{
	int		i;

	// Register all the clients in the server
	CL_UpdateLoading("CLIENTS");

	CL_LoadClientInfo(&cl.baseClientInfo, "unnamed\\male/grunt");

	for (i = 0; i < MAX_CLIENTS; i++)
	{
		if (!cl.configStrings[CS_PLAYERSKINS+i][0])
			continue;

		CL_LoadClientInfo(&cl.clientInfo[i], cl.configStrings[CS_PLAYERSKINS+i]);
	}
}


/*
 ==============================================================================

    LOADING

 ==============================================================================
*/


/*
 ==================
 CL_Loading

 TODO: This never gets called
 ==================
*/
void CL_Loading ()
{
	if (cls.loading)
		return;

	cls.loading = true;
	memset(&cls.loadingInfo, 0, sizeof(loadingInfo_t));

	// Force the console off
	Con_Close();
}

/*
 ==================
 CL_LoadClientInfo
 ==================
*/
void CL_LoadClientInfo (clientInfo_t *ci, const char *string)
{
	char	model[MAX_PATH_LENGTH], skin[MAX_PATH_LENGTH], name[MAX_PATH_LENGTH];
	char	checkMD2[MAX_PATH_LENGTH];
	char	checkTGA[MAX_PATH_LENGTH], checkPCX[MAX_PATH_LENGTH];
	char	*ch;
	int		i;

	// Rest client info
	memset(ci, 0, sizeof(clientInfo_t));

	// Isolate the player name
	Str_Copy(ci->name, string, sizeof(ci->name));
	ch = (char *)strchr(string, '\\');
	if (ch)
	{
		ci->name[ch-string] = 0;
		string = ch+1;
	}

	if (*string == 0)
	{
		// No custom skins or bad info string, so just use male/grunt
		ci->model = R_RegisterModel("players/male/tris.md2");
		ci->skin = R_RegisterMaterial("players/male/grunt", true);
		ci->icon = R_RegisterMaterialNoMip("players/male/grunt_i");
		ci->weaponModel[0] = R_RegisterModel("players/male/weapon.md2");

		// Save model/skin in the info string
		Str_SPrintf(ci->info, sizeof(ci->info), "male/grunt");

		ci->valid = true;
		return;
	}

	// Isolate model and skin name
	Str_Copy(model, string, sizeof(model));
	ch = strchr(model, '/');
	if (!ch)
		ch = strchr(model, '\\');
	if (ch)
	{
		*ch++ = 0;
		Str_Copy(skin, ch, sizeof(skin));
	}
	else
		skin[0] = 0;

	// If the model doesn't exist, default to male
	Str_SPrintf(checkMD2, sizeof(checkMD2), "players/%s/tris.md2", model);
	if (!FS_FileExists2(checkMD2))
		Str_Copy(model, "male", sizeof(model));

	// If the skin doesn't exist, default to male/grunt
	Str_SPrintf(checkTGA, sizeof(checkTGA), "players/%s/%s.tga", model, skin);
	Str_SPrintf(checkPCX, sizeof(checkPCX), "players/%s/%s.pcx", model, skin);
	if (!FS_FileExists2(checkTGA) && !FS_FileExists(checkPCX))
	{
		Str_Copy(model, "male", sizeof(model));
		Str_Copy(skin, "grunt", sizeof(skin));
	}

	// If the icon doesn't exist, default to male/grunt
	Str_SPrintf(checkTGA, sizeof(checkTGA), "players/%s/%s_i.tga", model, skin);
	Str_SPrintf(checkPCX, sizeof(checkPCX), "players/%s/%s_i.pcx", model, skin);
	if (!FS_FileExists2(checkTGA) && !FS_FileExists(checkPCX))
	{
		Str_Copy(model, "male", sizeof(model));
		Str_Copy(skin, "grunt", sizeof(skin));
	}

	// If a weapon model doesn't exist, default to male/grunt
	for (i = 0; i < cl.numWeaponModels; i++)
	{
		Str_SPrintf(checkMD2, sizeof(checkMD2), "players/%s/%s.md2", model, cl.weaponModels[i]);
		if (!FS_FileExists2(checkMD2))
		{
			Str_Copy(model, "male", sizeof(model));
			Str_Copy(skin, "grunt", sizeof(skin));
			break;
		}
	}

	// We can now load everything
	Str_SPrintf(name, sizeof(name), "players/%s/tris.md2", model);
	ci->model = R_RegisterModel(name);

	Str_SPrintf(name, sizeof(name), "players/%s/%s", model, skin);
	ci->skin = R_RegisterMaterial(name, true);

	Str_SPrintf(name, sizeof(name), "players/%s/%s_i", model, skin);
	ci->icon = R_RegisterMaterialNoMip(name);

	for (i = 0; i < cl.numWeaponModels; i++)
	{
		Str_SPrintf(name, sizeof(name), "players/%s/%s.md2", model, cl.weaponModels[i]);
		ci->weaponModel[i] = R_RegisterModel(name);
	}

	// Save model/skin in the info string
	Str_SPrintf(ci->info, sizeof(ci->info), "%s/%s", model, skin);

	ci->valid = true;
}

/*
 ==================
 CL_LoadGameMedia
 ==================
*/
void CL_LoadGameMedia ()
{
	int		time;

	time = Sys_Milliseconds();

	// Start precaching files
	cls.state = CS_LOADING;

	// Clear local effects because they now point to invalid files
	CL_ClearBeams();
	CL_ClearDynamicLights();
	CL_ClearParticles();

	// Get the map name for the loading screen
	Com_StripExtension(cl.configStrings[CS_MODELS+1]+5, cls.loadingInfo.map, sizeof(cls.loadingInfo.map));
	Str_Copy(cls.loadingInfo.name, cl.configStrings[CS_NAME], sizeof(cls.loadingInfo.name));

	// Register all the files for this level
	CL_RegisterCollisionMap();
	CL_RegisterSounds();
	CL_RegisterGraphics();
	CL_RegisterClients();

	// All the media is now loaded
	cls.state = CS_PRIMED;

	Com_Printf("CL_LoadGameMedia: %.2f seconds\n", (float)(Sys_Milliseconds() - time) / 1000.0f);

	CL_UpdateLoading("");
}

/*
 ==================
 CL_LoadLocalMedia
 ==================
*/
void CL_LoadLocalMedia ()
{
	// Get GL information
	cls2.glConfig = R_GetGLConfig();

	// Load a few needed materials
	cls.media.cinematicMaterial = R_RegisterMaterialNoMip("cinematic");
	cls.media.whiteMaterial = R_RegisterMaterialNoMip("white");
	cls.media.consoleMaterial = R_RegisterMaterialNoMip("console");
	cls.media.charsetMaterial = R_RegisterMaterialNoMip("charset");
	
	// Load the font
	cls.media.genericFont = R_RegisterFont("generic");
}


/*
 ==============================================================================

    DRAW LOADING

 ==============================================================================
*/


/*
 ==================
 CL_DrawLoading

 TODO: Add a placeholder background for now, also fix strings because the dont do NEWLINE
 ==================
*/
void CL_DrawLoading ()
{
	char	string[1024];

	switch (cls.state){
	case CS_CONNECTING:
		if (NET_IsLocalAddress(cls.serverAddress))
		{
			Str_SPrintf(string, sizeof(string), "Starting up...");
			CL_DrawString(5.0f, 15.0f, 0.18f, string, colorTable[COLOR_WHITE], cls.media.genericFont, 0);
		}
		else
		{
			if (cls.serverMessage[0])
				CL_DrawString(5.0f, 15.0f, 0.18f, cls.serverMessage, colorTable[COLOR_WHITE], cls.media.genericFont, 0);

			Str_SPrintf(string, sizeof(string), "Connecting to %s\nAwaiting connection... %i", cls.serverName, cls.connectCount);
			CL_DrawString(5.0f, 15.0f, 0.18f, string, colorTable[COLOR_WHITE], cls.media.genericFont, 0);
		}

		break;
	case CS_CHALLENGING:
		if (NET_IsLocalAddress(cls.serverAddress))
		{
			Str_SPrintf(string, sizeof(string), "Starting up...");
			CL_DrawString(5.0f, 15.0, 0.18f, string, colorTable[COLOR_WHITE], cls.media.genericFont, 0);
		}
		else
		{
			if (cls.serverMessage[0])
				CL_DrawString(5.0f, 15.0f, 0.18f, cls.serverMessage, colorTable[COLOR_WHITE], cls.media.genericFont, 0);

			Str_SPrintf(string, sizeof(string), "Connecting to %s\nAwaiting challenge... %i", cls.serverName, cls.connectCount);
			CL_DrawString(5.0f, 15.0f, 0.18f, string, colorTable[COLOR_WHITE], cls.media.genericFont, 0);
		}

		break;
	case CS_CONNECTED:
		// TODO: Check if we are downloading a file

		if (NET_IsLocalAddress(cls.serverAddress))
		{
			Str_SPrintf(string, sizeof(string), "Starting up...");
			CL_DrawString(5.0f, 15.0f, 0.18f, string, colorTable[COLOR_WHITE], cls.media.genericFont, 0);
		}
		else
		{
			Str_SPrintf(string, sizeof(string), "Connecting to %s\nAwaiting game state...", cls.serverName);
			CL_DrawString(5.0f, 15.0f, 0.18f, string, colorTable[COLOR_WHITE], cls.media.genericFont, 0);
		}

		break;
	case CS_LOADING:
		if (NET_IsLocalAddress(cls.serverAddress))
		{
			Str_SPrintf(string, sizeof(string), "Loading %s\n\"%s\"\n\n\nLoading... %s\n", cls.loadingInfo.map, cls.loadingInfo.name, cls.loadingInfo.string);
			CL_DrawString(5.0f, 15.0f, 0.18f, string, colorTable[COLOR_WHITE], cls.media.genericFont, 0);
		}
		else
		{
			Str_SPrintf(string, sizeof(string), "Loading %s\n\"%s\"\n\nConnecting to %s\nLoading... %s\n", cls.loadingInfo.map, cls.loadingInfo.name, cls.serverName, cls.loadingInfo.string);
			CL_DrawString(5.0f, 15.0f, 0.18f, string, colorTable[COLOR_WHITE], cls.media.genericFont, 0);
		}

		break;
	case CS_PRIMED:
		Str_SPrintf(string, sizeof(string), "Awaiting frame...");
		CL_DrawString(5.0f, 15.0f, 0.18f, string, colorTable[COLOR_WHITE], cls.media.genericFont, 0);

		break;
	}
}